using System;
using System.Collections.Generic;
using System.Text;
using Evolution.Engine.Interfaces;
using Microsoft.Xna.Framework;
using Evolution.Engine.Objects.Animation;
using Microsoft.Xna.Framework.Graphics;
using Evolution.Engine.Managers;

namespace Evolution.Engine.Objects
{
    public class SkinnedModel : BaseObject, Evolution.Engine.Interfaces.IUpdateable, IChildRenderer, ILoadable, IHasMaterial
    {
        private string _Asset;
        private Model _Model;
        protected string _Animation;
        protected SkinningData _AnimationSkinningData;
        protected AnimationPlayer _AnimationPlayer;
        protected AnimationClip _AnimationClip;

        public bool LoopAnimation { get { return _AnimationPlayer.LoopAnimation; } set { _AnimationPlayer.LoopAnimation = value; } }
        public string Animation { get { return _Animation; } set { _Animation = value; } }

        public void PlayAnimation(string animation, bool loop)
        {
            try
            {
                if (_AnimationPlayer.CurrentClip == null)
                {
                    _Animation = animation;
                    if (_AnimationSkinningData.AnimationClips.ContainsKey(_Animation))
                    {
                        _AnimationClip = _AnimationSkinningData.AnimationClips[_Animation];
                        _AnimationPlayer.StartClip(_AnimationClip);
                    }
                }
            }
            finally
            {
            }
        }

        public SkinnedModel(string asset, string name, string defaultAnimation)
            : base(name)
        {
            _Asset = asset;
            _Animation = defaultAnimation;
        }

        public override void LoadContent()
        {
            _Model = ResourceManager.Instance.LoadContent<Model>(_Asset);
            if(string.IsNullOrEmpty(_Animation)) throw new Exception("An animation must be specified");
            if(_Model.Tag == null) throw new Exception("Model containst no animation or it's not processed by SkinnedModelProcessor!");

            _AnimationSkinningData = _Model.Tag as SkinningData;
            _AnimationPlayer = new AnimationPlayer(_AnimationSkinningData);
            _AnimationClip = _AnimationSkinningData.AnimationClips[_Animation];
            _AnimationPlayer.StartClip(_AnimationClip);
            _AnimationPlayer.LoopAnimation = true;

            foreach (ModelMesh mesh in _Model.Meshes)
            {
                _BoundingSphere = mesh.BoundingSphere;
                _Bounds = BoundingBox.CreateFromSphere(_BoundingSphere);

                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.View = CameraManager.Instance.ActiveCamera.View;
                    effect.Projection = CameraManager.Instance.ActiveCamera.Projection;
                }
            }
        }

        public override void Update(GameTime gameTime)
        {
            Matrix world = Matrix.CreateScale(_Scale) *
                    Matrix.CreateFromQuaternion(_Rotation) *
                    Matrix.CreateTranslation(_Position);

            _AnimationPlayer.Update(gameTime.ElapsedGameTime, true, world);
        }

        public void RenderChildren(GameTime gameTimeB)
        {
            Shader shader = ShaderManager.Instance.GetShader(_Shader);

            shader.SetParameters(this);
            shader.SetParameter("Bones", _AnimationPlayer.GetSkinTransforms());

            foreach (ModelMesh mesh in _Model.Meshes)
            {
                GameEngine.Instance.Device.Indices = mesh.IndexBuffer;

                shader.Effect.Begin();
                foreach (EffectPass pass in shader.Effect.CurrentTechnique.Passes)
                {
                    pass.Begin();

                    foreach (ModelMeshPart part in mesh.MeshParts)
                    {
                        GameEngine.Instance.Device.VertexDeclaration = part.VertexDeclaration;
                        GameEngine.Instance.Device.Vertices[0].SetSource(
                            mesh.VertexBuffer,
                            part.StreamOffset,
                            part.VertexStride
                        );

                        GameEngine.Instance.Device.Textures[0] = ((BasicEffect)part.Effect).Texture;

                        GameEngine.Instance.Device.DrawIndexedPrimitives(
                            PrimitiveType.TriangleList,
                            part.BaseVertex, 0,
                            part.NumVertices,
                            part.StartIndex,
                            part.PrimitiveCount
                        );
                    }

                    pass.End();
                }
                shader.Effect.End();
            }
        }

        #region IEVOHasMaterial Members

        public void SetMaterialProperties()
        {
        }

        #endregion
    }
}
